package com.kexio.security.service.impl;

import java.time.LocalDateTime;
import java.util.UUID;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import com.kexio.core.security.context.UserContext;
import com.kexio.core.security.context.UserContextHolder;
import com.kexio.core.security.jwt.JwtTokenProvider;
import com.kexio.core.security.jwt.JwtTokenProvider.TokenPair;
import com.kexio.core.security.jwt.JwtTokenValidator;
import com.kexio.core.security.service.AuthenticationService;
import com.kexio.core.security.service.UserContextService;
import com.kexio.security.domain.User;
import com.kexio.security.domain.enums.UserStatus;
import com.kexio.security.service.UserRepository;
import com.kexio.security.service.SecurityUserContextService;

/**
 * Security模块认证服务实现
 * 实现core starter提供的AuthenticationService接口
 * 
 * @author kexio
 */
@Service
public class SecurityAuthenticationService implements AuthenticationService {

    private static final Logger log = LoggerFactory.getLogger(SecurityAuthenticationService.class);

    private final JwtTokenProvider jwtTokenProvider;
    private final JwtTokenValidator jwtTokenValidator;
    private final UserContextService userContextService;
    private final UserRepository userRepository;
    private final PasswordEncoder passwordEncoder;

    public SecurityAuthenticationService(JwtTokenProvider jwtTokenProvider,
                                        JwtTokenValidator jwtTokenValidator,
                                        SecurityUserContextService userContextService,
                                        UserRepository userRepository) {
        this.jwtTokenProvider = jwtTokenProvider;
        this.jwtTokenValidator = jwtTokenValidator;
        this.userContextService = userContextService;
        this.userRepository = userRepository;
        this.passwordEncoder = new BCryptPasswordEncoder();
    }

    @Override
    public TokenPair authenticate(String username, String password) {
        return authenticate(username, password, 1L); // 默认租户ID
    }

    @Override
    public TokenPair authenticate(String username, String password, Long tenantId) {
        if (username == null || username.trim().isEmpty()) {
            throw new IllegalArgumentException("用户名不能为空");
        }
        if (password == null || password.trim().isEmpty()) {
            throw new IllegalArgumentException("密码不能为空");
        }

        try {
            // 验证用户密码
            if (!verifyPassword(username, password)) {
                log.warn("用户 {} 登录失败: 用户名或密码错误", username);
                throw new IllegalArgumentException("用户名或密码错误");
            }

            // 加载用户信息
            UserContext userContext = userContextService.loadUserContext(username);
            if (userContext == null) {
                log.warn("用户 {} 登录失败: 用户不存在", username);
                throw new IllegalArgumentException("用户不存在");
            }

            // 设置登录信息
            userContext.setTenantId(tenantId);
            userContext.setLoginTime(LocalDateTime.now());
            userContext.setSessionId(UUID.randomUUID().toString());

            // 更新用户最后登录信息
            updateLastLoginInfo(userContext);

            // 生成令牌对
            TokenPair tokenPair = jwtTokenProvider.generateTokenPair(userContext);

            // 设置用户上下文
            userContextService.updateUserContext(userContext);

            log.info("用户 {} 登录成功, tenantId={}", username, tenantId);
            return tokenPair;
        } catch (Exception e) {
            log.error("用户 {} 登录过程中出现异常: {}", username, e.getMessage(), e);
            throw new RuntimeException("登录失败: " + e.getMessage(), e);
        }
    }

    @Override
    public UserContext validateAccessToken(String accessToken) {
        if (accessToken == null || accessToken.trim().isEmpty()) {
            return null;
        }

        try {
            return jwtTokenValidator.validateAndExtract(accessToken);
        } catch (Exception e) {
            log.debug("验证访问令牌失败: {}", e.getMessage());
            return null;
        }
    }

    @Override
    public boolean validateRefreshToken(String refreshToken) {
        if (refreshToken == null || refreshToken.trim().isEmpty()) {
            return false;
        }

        try {
            return jwtTokenValidator.validateRefreshToken(refreshToken);
        } catch (Exception e) {
            log.debug("验证刷新令牌失败: {}", e.getMessage());
            return false;
        }
    }

    @Override
    public String refreshAccessToken(String refreshToken) {
        if (!validateRefreshToken(refreshToken)) {
            throw new IllegalArgumentException("无效的刷新令牌");
        }

        try {
            // 从刷新令牌中获取用户名
            String username = jwtTokenValidator.getUsername(refreshToken);
            if (username == null) {
                throw new IllegalArgumentException("无法从刷新令牌中获取用户信息");
            }

            // 加载用户最新信息
            UserContext userContext = userContextService.loadUserContext(username);
            if (userContext == null) {
                throw new IllegalArgumentException("用户不存在或已被禁用");
            }

            // 生成新的访问令牌
            String newAccessToken = jwtTokenProvider.refreshAccessToken(refreshToken, userContext);
            
            log.debug("为用户 {} 刷新访问令牌成功", username);
            return newAccessToken;
        } catch (Exception e) {
            log.warn("刷新访问令牌失败: {}", e.getMessage());
            throw new RuntimeException("刷新令牌失败: " + e.getMessage(), e);
        }
    }

    @Override
    public boolean logout(String accessToken) {
        try {
            UserContext userContext = validateAccessToken(accessToken);
            if (userContext != null) {
                // 清除用户上下文
                userContextService.clearUserContext();
                
                // TODO: 将令牌加入黑名单或从缓存中移除
                log.info("用户 {} 登出成功", userContext.getUsername());
                return true;
            }
            return false;
        } catch (Exception e) {
            log.warn("用户登出失败: {}", e.getMessage());
            return false;
        }
    }

    @Override
    public boolean isLoggedIn() {
        return userContextService.isAuthenticated();
    }

    @Override
    public UserContext getCurrentUser() {
        return UserContextHolder.getContext();
    }

    @Override
    public boolean verifyPassword(String username, String rawPassword) {
        try {
            User user = userRepository.findByUsername(username).orElse(null);
            if (user == null) {
                log.debug("用户 {} 不存在", username);
                return false;
            }

            if (!UserStatus.ACTIVE.getCode().equals(user.getStatus())) {
                log.debug("用户 {} 已被禁用", username);
                return false;
            }

            boolean matches = passwordEncoder.matches(rawPassword, user.getPassword());
            if (!matches) {
                log.debug("用户 {} 密码验证失败", username);
            }
            return matches;
        } catch (Exception e) {
            log.error("验证用户 {} 密码时发生异常: {}", username, e.getMessage(), e);
            return false;
        }
    }

    @Override
    public boolean changePassword(String username, String oldPassword, String newPassword) {
        if (username == null || username.trim().isEmpty()) {
            throw new IllegalArgumentException("用户名不能为空");
        }
        if (oldPassword == null || oldPassword.trim().isEmpty()) {
            throw new IllegalArgumentException("旧密码不能为空");
        }
        if (newPassword == null || newPassword.trim().isEmpty()) {
            throw new IllegalArgumentException("新密码不能为空");
        }

        try {
            // 验证旧密码
            if (!verifyPassword(username, oldPassword)) {
                log.warn("用户 {} 修改密码失败: 旧密码错误", username);
                return false;
            }

            User user = userRepository.findByUsername(username).orElse(null);
            if (user == null) {
                return false;
            }

            // 加密新密码并保存
            String encryptedPassword = passwordEncoder.encode(newPassword);
            user.setPassword(encryptedPassword);
            user.setUpdatedTime(LocalDateTime.now());
            userRepository.save(user);
            
            log.info("用户 {} 密码修改成功", username);
            return true;
        } catch (Exception e) {
            log.error("用户 {} 修改密码失败: {}", username, e.getMessage(), e);
            return false;
        }
    }

    @Override
    public boolean resetPassword(String username, String newPassword) {
        if (username == null || username.trim().isEmpty()) {
            throw new IllegalArgumentException("用户名不能为空");
        }
        if (newPassword == null || newPassword.trim().isEmpty()) {
            throw new IllegalArgumentException("新密码不能为空");
        }

        try {
            User user = userRepository.findByUsername(username).orElse(null);
            if (user == null) {
                log.warn("用户 {} 不存在，无法重置密码", username);
                return false;
            }

            // 加密新密码并保存
            String encryptedPassword = passwordEncoder.encode(newPassword);
            user.setPassword(encryptedPassword);
            user.setUpdatedTime(LocalDateTime.now());
            userRepository.save(user);
            
            log.info("用户 {} 密码重置成功", username);
            return true;
        } catch (Exception e) {
            log.error("用户 {} 重置密码失败: {}", username, e.getMessage(), e);
            return false;
        }
    }

    /**
     * 更新用户最后登录信息
     * 
     * @param userContext 用户上下文
     */
    private void updateLastLoginInfo(UserContext userContext) {
        try {
            User user = userRepository.findByUsername(userContext.getUsername()).orElse(null);
            if (user != null) {
                user.setLastLoginTime(userContext.getLoginTime());
                user.setLastLoginIp(userContext.getLoginIp());
                user.setLoginCount(user.getLoginCount() == null ? 1 : user.getLoginCount() + 1);
                user.setUpdatedTime(LocalDateTime.now());
                userRepository.save(user);
            }
        } catch (Exception e) {
            log.warn("更新用户 {} 最后登录信息失败: {}", userContext.getUsername(), e.getMessage());
        }
    }
}
